/* 
  Copyright (c) SilverLining 2015
  Date:    2016-1-7
  File:    Archiver.cpp  
  Description: 

*/

#include "stdafx.h"

using namespace System;
using namespace System::Security;
using namespace System::Runtime::Serialization;
using namespace System::Runtime::InteropServices;

#include "shim_object.h"
#include "GameObject.h"
#include "Archiver.h"
#include "BadResultException.h"

using namespace m;
using namespace m::alloc;

namespace SL
{
	namespace M
	{
		Archiver::Archiver()
			: _Archiver(new m::archiver())
		{
		}
		Archiver::Archiver(m::archiver * archiver)
			: _Archiver(archiver)
		{
			assert(archiver);
			if (archiver)
			{
				archiver->reference();
			}
		}
		
		Archiver::~Archiver()
		{
			Archiver::!Archiver();
		}

		Archiver::!Archiver()
		{
			if (_Archiver)
			{
				_Archiver->release();
			}
		}

		m::archiver* Archiver::NativeArchiver::get()
		{
			return _Archiver;
		}

		void Archiver::Archive(GameObject ^ root, m::serializer * serializer)
		{
			if (root == nullptr)
			{
				throw gcnew ArgumentNullException("root");
			}
			if (serializer == nullptr)
			{
				throw gcnew ArgumentNullException("serializer");
			}

			try
			{
				_Archiver->archive(root->NativePtr, serializer);
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
		}

		void M::Archiver::Unarchive(GameObject ^ root, m::serializer * serializer)
		{
			if (root == nullptr)
			{
				throw gcnew ArgumentNullException("root");
			}
			if (serializer == nullptr)
			{
				throw gcnew ArgumentNullException("serializer");
			}

			try
			{
				_Archiver->unarchive(root->NativePtr, serializer);
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
		}

		void SL::M::Archiver::WriteArrayStart(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_array_start(nameChars);
		}

		void SL::M::Archiver::WriteArrayStart()
		{
			_Archiver->write_array_start();
		}

		void SL::M::Archiver::WriteArrayValue(GameObject ^ obj)
		{
			if (obj)
			{
				_Archiver->write_array_value(obj->NativePtr);
			}
			else
			{
				_Archiver->write_array_value((m::object*)nullptr);
			}
		}

		void SL::M::Archiver::WriteArrayValue(double num)
		{
			_Archiver->write_array_value(num);
		}

		void SL::M::Archiver::WriteArrayValue(signed char num)
		{
			_Archiver->write_array_value(num);
		}

		void SL::M::Archiver::WriteArrayValue(Int16 num)
		{
			_Archiver->write_array_value(num);
		}

		void SL::M::Archiver::WriteArrayValue(Int32 num)
		{
			_Archiver->write_array_value(num);
		}

		void SL::M::Archiver::WriteArrayValue(Int64 num)
		{
			_Archiver->write_array_value(num);
		}

		void SL::M::Archiver::WriteArrayValue(String ^ string)
		{
			if (string)
			{
				pin_ptr<const wchar_t> stringChars = PtrToStringChars(string);
				_Archiver->write_array_value(stringChars);
			}
			else
			{
				_Archiver->write_array_value((const wchar_t*)nullptr);
			}
		}

		void SL::M::Archiver::WriteArrayValue(bool bit)
		{
			_Archiver->write_array_value(bit);
		}

		void SL::M::Archiver::WriteArrayBytes(array<byte>^ data)
		{
			size_t length = 0;
			pin_ptr<Byte> pData;
			byte* dataPtr = nullptr;
			if (data != nullptr)
			{
				length = data->Length;
				dataPtr = new byte[data->Length];
				pData = &data[0];
				dataPtr = pData;
			}
			_Archiver->write_array_value(dataPtr, length);
		}

		void SL::M::Archiver::WriteArrayEnd()
		{
			_Archiver->write_array_end();
		}

		void SL::M::Archiver::WriteValue(String ^ name, GameObject ^ obj)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			if (obj)
			{
				_Archiver->write_value(nameChars, obj->NativePtr);
			}
			else
			{
				_Archiver->write_value(nameChars, (object*)nullptr);
			}
		}

		void SL::M::Archiver::WriteValue(String ^ name, double num)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_value(nameChars, num);
		}

		void SL::M::Archiver::WriteValue(String ^ name, signed char num)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_value(nameChars, num);
		}

		void SL::M::Archiver::WriteValue(String ^ name, Int16 num)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_value(nameChars, num);
		}

		void SL::M::Archiver::WriteValue(String ^ name, Int32 num)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_value(nameChars, num);
		}

		void SL::M::Archiver::WriteValue(String ^ name, Int64 num)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_value(nameChars, num);
		}

		void SL::M::Archiver::WriteValue(String ^ name, String ^ string)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			if (string)
			{
				pin_ptr<const wchar_t> stringChars = PtrToStringChars(string);
				_Archiver->write_value(nameChars, stringChars);
			}
			else
			{
				_Archiver->write_value(nameChars, (const wchar_t*)nullptr);
			}
		}

		void SL::M::Archiver::WriteValue(String ^ name, bool val)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			_Archiver->write_value(nameChars, val);
		}

		void SL::M::Archiver::WriteBytes(String ^ name, array<byte>^ data)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			size_t length = 0;
			pin_ptr<Byte> pData = &data[0];
			if (data != nullptr)
			{
				length = data->Length;
			}
			_Archiver->write_value(nameChars, (const byte*)pData, length);
		}

		bool SL::M::Archiver::ReadArrayStart(String ^ name, int % sz)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			size_t szVar;
			if (_Archiver->read_array_start(nameChars, &szVar))
			{
				if (szVar > INT32_MAX)
				{
					throw gcnew IndexOutOfRangeException("The array is too large.");
				}
				sz = (int)szVar;
				return true;
			}
			else
			{
				sz = 0;
				return false;
			}
		}

		bool SL::M::Archiver::ReadArrayStart(int index, int % sz)
		{
			size_t szVar;
			if (_Archiver->read_array_start(index, &szVar))
			{
				if (szVar > INT32_MAX)
				{
					throw gcnew IndexOutOfRangeException("The array is too large.");
				}
				sz = (int)szVar;
				return true;
			}
			else
			{
				sz = 0;
				return false;
			}
		}

		generic <class T> where T : GameObject
		void Archiver::ReadArrayObject(int index, T% obj)
		{
			ref_storage track1;
			shim_object* shimObj = nullptr;
			if (obj != nullptr)
			{
				assign(shimObj, obj->NativePtr);
			}
			_Archiver->read_array_value(index, &shimObj);
			if (shimObj)
			{
				obj = safe_cast<T>(shimObj->get_object());
			}
			clear(shimObj);
		}

		signed char SL::M::Archiver::ReadArraySByte(int index)
		{
			int8_t numVar;
			_Archiver->read_array_value(index, &numVar);
			return numVar;
		}

		int16_t Archiver::ReadArrayShort(int index)
		{
			int16_t numVar;
			_Archiver->read_array_value(index, &numVar);
			return numVar;
		}

		int32_t SL::M::Archiver::ReadArrayInt(int index)
		{
			int32_t numVar;
			_Archiver->read_array_value(index, &numVar);
			return numVar;
		}

		int64_t SL::M::Archiver::ReadArrayLong(int index)
		{
			int64_t numVar;
			_Archiver->read_array_value(index, &numVar);
			return numVar;
		}

		String ^ SL::M::Archiver::ReadArrayString(int index)
		{
			wchar_t* strVar = nullptr;
			_Archiver->read_array_value(index, &strVar);
			String^ ret = nullptr;
			if (strVar)
			{
				ret = gcnew String(strVar);
			}
			clear(strVar);
			return ret;
		}

		double Archiver::ReadArrayDouble(int index)
		{
			double numVar;
			_Archiver->read_array_value(index, &numVar);
			return numVar;
		}

		bool SL::M::Archiver::ReadArrayBool(int index)
		{
			bool bitVar;
			_Archiver->read_array_value(index, &bitVar);
			return bitVar;
		}

		array<byte>^ SL::M::Archiver::ReadArrayBytes(int index)
		{
			byte* byteArray = nullptr;
			size_t sz;
			_Archiver->read_array_value(index, &byteArray, &sz);
			array<byte>^ data = nullptr;
			if (byteArray)
			{
				if (sz > INT32_MAX)
				{
					throw gcnew IndexOutOfRangeException("Byte array too large.");
				}

				data = gcnew array<byte>((int)sz);
				pin_ptr<Byte> pData = &data[0];
				memcpy(pData, byteArray, (int)sz);
				delete[] byteArray;
			}
			return data;
		}

		void SL::M::Archiver::ReadArrayEnd()
		{
			_Archiver->read_array_end();
		}

		generic <class T> where T : GameObject
		void SL::M::Archiver::ReadObject(String ^ name, T% obj)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			shim_object* shimObj = nullptr;
			if (obj != nullptr)
			{
				assign(shimObj, obj->NativePtr);
			}
			_Archiver->read_value(nameChars, &shimObj);
			if (shimObj)
			{
				obj = safe_cast<T>(shimObj->get_object());
			}
			clear(shimObj);
		}

		double SL::M::Archiver::ReadDouble(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			double numVal;
			_Archiver->read_value(nameChars, &numVal);
			return numVal;
		}

		signed char SL::M::Archiver::ReadSByte(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			int8_t numVal;
			_Archiver->read_value(nameChars, &numVal);
			return numVal;
		}

		int16_t SL::M::Archiver::ReadShort(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			int16_t numVal;
			_Archiver->read_value(nameChars, &numVal);
			return numVal;
		}

		int32_t SL::M::Archiver::ReadInt(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			int32_t numVal;
			_Archiver->read_value(nameChars, &numVal);
			return numVal;
		}

		int64_t SL::M::Archiver::ReadLong(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			int64_t numVal;
			_Archiver->read_value(nameChars, &numVal);
			return numVal;
		}

		String^ SL::M::Archiver::ReadString(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			wchar_t* strVar = nullptr;
			_Archiver->read_value(nameChars, &strVar);
			String^ ret = nullptr;
			if (strVar)
			{
				ret = gcnew String(strVar);
			}
			clear(strVar);
			return ret;
		}

		bool SL::M::Archiver::ReadBool(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			bool boolVal;
			_Archiver->read_value(nameChars, &boolVal);
			return boolVal;
		}

		array<byte>^ SL::M::Archiver::ReadBytes(String ^ name)
		{
			pin_ptr<const wchar_t> nameChars = PtrToStringChars(name);
			byte* byteArray = nullptr;
			size_t sz;
			_Archiver->read_value(nameChars, &byteArray, &sz);
			array<byte>^ data = nullptr;
			if (byteArray)
			{
				if (sz > INT32_MAX)
				{
					sz = INT32_MAX;
				}

				data = gcnew array<byte>((int)sz);
				pin_ptr<Byte> pData = &data[0];
				memcpy(pData, byteArray, (int)sz);
				delete[] byteArray;
			}
			return data;
		}
	}
}